export const PAGE_START = 1
export const DEFAULT_PAGE_SIZE = 10

export interface Page<E> extends Slice<E> {
    totalElements: number
    totalPages: number
}

export interface Slice<E> {
    content: Array<E>
    first: boolean
    last: boolean
    empty: boolean
    number: number
    numberOfElements: number
    size: number
}

export interface Slicination {
    page: number
    size: number
    last: boolean
    first: boolean
}

export interface Pagination extends Slicination {
    total: number
}

// TODO to object

export interface PageableRequest {
    page: number
    size: number
    sort: string[]
}

export class Pageable {
    page: number
    size: number
    sort: Sort

    constructor(page: number = PAGE_START, size: number = DEFAULT_PAGE_SIZE, sort: Sort = Sort.empty) {
        this.page = page;
        this.size = size;
        this.sort = sort;
    }

    static from(pageable: Pageable, options: {
        page?: number,
        size?: number,
        sort?: Sort,
    }): Pageable {
        return new Pageable(options.page ?? pageable.page, options.size ?? pageable.size, options.sort ?? pageable.sort)
    }

    toRequestParams(): PageableRequest {
        return {
            page: this.page,
            size: this.size,
            sort: this.sort.toRequestParam(),
        }
    }

    toDependencyList() {
        return [this.page, this.size, this.sort.toString()]
    }

    get offset(){
        return (this.page - PAGE_START) * this.size
    }

    get limit(){
        return this.size
    }

    get next(){
        return Pageable.from(this,{page: this.page + 1})
    }

    get prev(){
        return Pageable.from(this,{page: Math.max(this.page - 1,PAGE_START)})
    }

    get first(){
        return Pageable.from(this,{page: PAGE_START})
    }

    toString():string{
        return `Pageable(page: ${this.page}, size:${this.size}, sort:${this.sort.toString()})`
    }

}

export function defaultPagination(
    options: { page?: number; size?: number } = {},
): Pagination {
    return {
        ...defaultSlicination(options),
        total: 0,
    }
}

export function defaultSlicination(
    options: {
        page?: number
        size?: number
    } = {},
): Slicination {
    return {
        page: options.page || PAGE_START,
        size: options.size || DEFAULT_PAGE_SIZE,
        first: true,
        last: false,
    }
}

export function slicinationToPageable(slicination:Slicination){
  return new Pageable(slicination.page, slicination.size)
}

export function paginationToPageable(pagination: Pagination): Pageable {
    return slicinationToPageable(pagination)
}

export function pageToPagination(page: Page<any>, pageable: Pageable): Pagination {
    return {
        ...pageable,
        ...page,
        total: page.totalElements
    }
}

export class Order {
    name: string
    ascend?: boolean

    constructor(name: string, ascend?: boolean) {
        this.name = name;
        this.ascend = ascend;
    }

    toString() {
        return `${this.name}${this.ascend === undefined ? "" : `,${this.ascend ? "asc" : "desc"}`}`
    }
}

export class Sort {
    readonly orders: Array<Order>
    static empty = new Sort([])

    toString() {
        return this.orders.join("|")
    }

    constructor(orders: Array<Order>) {
        this.orders = orders;
    }

    toRequestParam(){
        return this.orders.map(it => it.toString())
    }
}
